Software engineers use many techniques, tools, and practices in order build robust software. One such software engineering technique is use of abstraction layers, which are used to translate internal program data structures into interface-oriented data structures. A standard technique for creating a data structure abstraction in programming languages is to provide only a forward declaration of a data structure in public header files, while keeping the corresponding struct definition in a separate private header file.
For example, a programmer who wishes to know the file descriptor corresponding to a FILE data structure can use an appropriate function to obtain the descriptor value rather than dereferencing a member of the corresponding struct directly. By abstracting the details of the data structure implementation away from client programs that call the function, the programmer retains the freedom to change the implementation, content, and data layout of the data structure.
Another example of a technique to improve the robustness of software uses a system call instead of using a function call as above. This technique is to design a program so that the implementation of the program can evolve and change over time, while still maintaining a consistent set of external program interfaces. These same interfaces are offered with each release of the operating system, even though the operating system's internal data structures and implementation may vary as new releases are offered.
Another software engineering practice used to build robust software is the use of tracing frameworks and debuggers. A tracing framework is a collection of software routines and tools that permit a user to instrument and record the activity of one or more executing programs, such as an operating system kernel. The user may provide to the tracing framework instrumentation requests that obtain information about the activity of the executing programs. Tracing frameworks typically permit users to describe instrumentation requests by naming one or more probes, which are locations in the executing program. The probes are used as data-recording sites.
Associated with a probe is one or more actions that describe what is to be done when program execution passes through the probe. Tracing frameworks usually provide either a defined set of actions at each probe, a set of user-selectable actions, or the ability to execute an arbitrary set of actions that are written in a high-level programming language. In tracing frameworks that support a programming language for defining actions, language statements are compiled into an intermediate form, or directly into binary code (i.e., object code), and then executed when the probe is triggered by execution of the instrumented program.
Because probes are locations of instrumentation within the executing instrumented program, the probes are therefore associated with the implementation and an embodiment of the executing program. The tracing framework actions provide access to data memory associated with the instrumented program. The data memory is arranged into a set of implementation data structures associated with the internal implementation details and artifacts of the instrumented program.
A debugger is another tool that allows inspection of an instrumented program executing under complete or partial control of the debugger. A debugger is distinct from a tracing framework in that a debugger typically provides a mechanism whereby the instrumented program stops executing at instrumented locations of interest and then is inspected by a user of the debugger before subsequently continuing execution. In contrast, a tracing framework typically permits users to perform additional actions only while the instrumented program is executing. Examination of the results occurs after the execution is complete, or in parallel with execution of the instrumented program. Debuggers are similar to tracing frameworks in that both offer a facility whereby the user may view the data memory of the instrumented program and control the format and organization of the memory display.